/*-
 * Copyright (c) 2006 Vishal Patil <bsd.devil@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <assert.h>
#include <strings.h>
#include <stdlib.h>
#include "iscsi_pdu.h"
#include "iscsi_pdu_list.h"

/*
 *	PDU List head
 */
struct {
	uint32_t max_pdus;
	uint32_t count;
	struct iscsi_pdu_incore * head;
} pdu_list;

/* Initialize the iSCSI pdu list */
int 
iscsi_pdu_list_init(uint32_t max_pdus) {
	
	int i = 0;
	struct iscsi_pdu_incore * pdu;
	
	pdu_list.max_pdus = max_pdus;
	pdu_list.head 	= NULL;
	pdu_list.count	= 0;

	for (i = 0; i < max_pdus ; i++) {
		pdu = (struct iscsi_pdu_incore *)
			malloc(sizeof(struct iscsi_pdu_incore));
		if (!pdu) {
			return -1;
		}
		bzero(pdu,sizeof(*pdu));
		put_pdu(pdu);
	}

	assert(pdu_list.count == pdu_list.max_pdus);
	return 0;
}

/* 
 * Get a free iSCSI pdu. hlength_max is the maximum size of the 
 * AHS while dlength_max is the maximum size of the data segment
 */
struct iscsi_pdu_incore * 
get_pdu(int hlength_max, int dlength_max) {
	struct iscsi_pdu_incore * pdu;

	pdu = pdu_list.head;
	pdu_list.head = pdu->next;
	
	pdu->next = NULL;
	bzero(pdu,sizeof(*pdu));

	if (hlength_max) {
		pdu->ahs = (struct iscsi_ahs *)
				malloc(sizeof(hlength_max));
		pdu->hlength_max = hlength_max;
		bzero(pdu->ahs,hlength_max);
	}

	if (dlength_max) {
		pdu->data_seg = (uint8_t *)malloc(sizeof(dlength_max));
		pdu->dlength_max = dlength_max;
		bzero(pdu->data_seg,dlength_max);
	}
		
	return pdu;
}

/* Return back a pdu */
void
put_pdu(struct iscsi_pdu_incore * pdu) {
	assert(pdu->next == NULL);
	pdu->next = pdu_list.head;
	pdu_list.head = pdu;
	pdu_list.count++;	
}

/* Destory the iSCSI pdu list */
int 
iscsi_pdu_list_destroy() {
	struct iscsi_pdu_incore * pdu;
	struct iscsi_pdu_incore * prev_pdu;

	pdu = pdu_list.head;

	while (pdu) {
		prev_pdu = pdu;
		pdu = pdu->next;

		if (prev_pdu->ahs) {
			free(prev_pdu->ahs);
		}

		if (prev_pdu->data_seg) {
			free(prev_pdu->data_seg);
		}

		free(prev_pdu);
	}

	pdu_list.head 	= NULL;
	pdu_list.count	= 0;

	return 0;
}


